﻿using System;
using System.Linq;
using System.Transactions;
using System.Collections.Generic;
using Newtonsoft.Json;
using T.FAS.Runtime.DataModel.Dao;
using T.FAS.Runtime.Metadata.Entity.DataModel;
using T.FAS.Runtime.Metadata.Entity;
using T.FAS.Runtime.Base.Entity;
using T.FAS.Runtime.Metadata.SPI;
using  T.ServiceFramework.ServiceGateway.ClientExtension;
using T.ServiceFramework.ServiceGateway.Client;
//using T.FAS.Authority.MetaData.Dao;
//using T.FAS.Authority.MetaData.Dev.Plugin;
//using T.BDP.DCP.MetaData.Entity;

namespace T.FAS.Runtime.DataModel.Plugin
{
    public class DataQueryRTPManager : AbsRTPMetaDataManager
    {
        public const string MetaDataID = DataModelConst.DQMetaType;
        public const string MetaDataName = "数据查询";
        private readonly DataQueryLogic _dataQueryLogic = new DataQueryLogic();
        //private readonly ActionPermissionDefinitionManager _permissionMgr = new ActionPermissionDefinitionManager();
        private const string BigDataPublishSG = "DCP-DcpObjectPublish";
        private const string BigDataPublishSG_Inner = "DCP-DcpObjectPublish-Inner";

        public override IMetaDataContent GetMetaDataContent(string metaDataId)
        {
            if (string.IsNullOrEmpty(metaDataId))
                throw new FasException(DataModelErrorCode.NullValue, "元数据id为空");
            var dataquery = _dataQueryLogic.GetDataQueryByID(metaDataId);
            if (dataquery == null)
                throw new FasException(DataModelErrorCode.DQ.RetrieveError, $"DQ:{metaDataId}元数据不存在，请检查！");
            dataquery.MetaDataID   = dataquery.ID;
            dataquery.MetaDataCode = dataquery.Code;
            dataquery.MetaDataName = dataquery.Name;
            //等待震哥更新RTPClient方法后，将数据组装部分放到Deserialize中，减少缓存Json大小以及缓存的旧数据
            //PackagingDependencyMetaData(dataquery);

            return dataquery;
        }

        public override string SerializeObject(IMetaDataContent metadata, MetadataSerializeType serializeType)
        {
            if (serializeType == MetadataSerializeType.JSON)
            {
                //(metadata as DataQuery).ClearRef();
                return JsonConvert.SerializeObject(metadata, new JsonSerializerSettings
                {
                    ReferenceLoopHandling = ReferenceLoopHandling.Ignore
                });
            }
            else
            {
                throw new NotImplementedException();
            }
        }

        public override IMetaDataContent DeserializeObject(string metaDataString, MetadataSerializeType serializeType)
        {
            if (serializeType == MetadataSerializeType.JSON)
            {
                return DeserializeObject(metaDataString, true);
            }
            else
            {
                throw new NotImplementedException();
            }
        }
        public override IMetaDataContent DeserializeObject(string metaDataString, bool isPackingData = false)
        {
            if (string.IsNullOrEmpty(metaDataString))
                throw new FasException(DataModelErrorCode.Error, $"DQ反序列化Json不能为空，请检查！");
            var cdo = JsonConvert.DeserializeObject<DataQuery>(metaDataString, new JsonSerializerSettings
            {
                ReferenceLoopHandling = ReferenceLoopHandling.Ignore
            });

            if (isPackingData)
                PackagingDependencyMetaData(cdo);

            return cdo;
        }

        public override void Import(List<IMetaDataContent> metaDatas)
        {
            var dqList = new List<DataQuery>();
            foreach (var metaData in metaDatas)
            {
                var dataQuery = metaData as DataQuery;
                if (dataQuery == null)
                {
                    throw new FasException(DataModelErrorCode.ParamError, "元数据类型不是DataQuery, 实际类型:" + metaData.GetType().FullName);
                }

                dqList.Add(dataQuery);
            }
            try
            {
                List<string> ids = new List<string>();
                //处理大数据dq发布、权限发布
                foreach (var dq in dqList)
                {
                    ids.Add(dq.MetaDataID);
                    if (DataQueryHelper.IsBigDataQuerySource(dq))
                    {
                        PublishToBigDataSet(dq);
                    }
                    /* Todo
                    if (dq.IsPermissionControl)
                        _permissionMgr.Import(dq.ActionPermission, MetaDataImportStrategy.Replace);
                    */
                }

                //批量插入dq
                _dataQueryLogic.BatchDeleteDataQuery(ids);
                _dataQueryLogic.BatchSaveDataQuery(dqList);
            }
            catch (Exception e)
            {
                throw new FasException(DataModelErrorCode.DQ.ImportError, "DataQuery元数据导入失败", e);
            }
        }

        public override void Delete(List<string> IDs)
        {
            if (IDs == null)
                return;
            IDs = IDs.Where(id => id != null).ToList();
            try
            {
                using (var scope = new TransactionScope(TransactionScopeOption.Required, new TransactionOptions() { IsolationLevel = System.Transactions.IsolationLevel.ReadCommitted }))
                {
                    foreach (var id in IDs)
                    {   
                        /* Todo
                        _permissionMgr.Import(new ActionPermissionDefinitionForView { ActionID = id }, MetaDataImportStrategy.Delete);
                        */
                        _dataQueryLogic.DeleteDataQuery(id);
                    }
                    scope.Complete();
                }
            }
            catch (Exception e)
            {
                throw new FasException(DataModelErrorCode.DQ.ImportError, $"{MetaDataName}元数据导入时删除失败", e);
            }
        }

        public void PackagingDependencyMetaData(IMetaDataContent metaData)
        {
            InitializeDQ(metaData as DataQuery);
        }

        /// <summary>
        /// 初始化填充DQ的引用类型属性
        /// </summary>
        /// <param name="dataquery"></param>
        public void InitializeDQ(DataQuery dataquery, bool isSubQuery = false)
        {
            if (dataquery == null)
                return;
            var unifyCode = DataModelErrorCode.GetUnifiedErrorCode(MetaDataID, dataquery.ID, dataquery.Code);
            /* Todo
            //大数据DQ只需要给DataAsset对象赋值即可
            if (dataquery.IsBigDataQuerySource())
            {
                foreach (var source in dataquery.DataQuerySources)
                {
                    if (source.DataAsset == null)
                        source.DataAsset = GetMetaData<DataAsset>(source.DataObjectID);
                }
                return;
            }*/
                        try
            {
                //为datasource填充DO实例
                foreach (var source in dataquery.DataQuerySources)
                {
                    if (source.DataObject == null)  //兼容json反序列化处理，如果source.DataObject不为空，则为反序列化出来的DO，直接处理即可
                        source.DataObject = GetMetaData<DataObject>(source.DataObjectID, false);
                    if (source.DataObject == null)
                        throw new FasException(DataModelErrorCode.DQ.InitError, $"{unifyCode}获取dataObject:{source.DataObjectID}实体失败，请检查！");
                }
                var dqSources = dataquery.DataQuerySources.Select(s => s.DataObject).ToList();
                //为datacolumn填充实例
                foreach (var column in dataquery.DataQueryColumns)
                {
                    if (column.ColumnType == ColumnType.Normal)
                    {
                        column.DataObject = dqSources.Find(o => o.ID == column.DataObjectID);
                        column.DataColumn = column.DataObject.Columns.Find(col => col.ID == column.DataObjectColumnID);
                       
                    }
                    else
                    {
                        //聚合列也需要填充DO及列信息
                        if (!string.IsNullOrEmpty(column.DataObjectID))
                        {
                            column.DataObject = dqSources.Find(o => o.ID == column.DataObjectID);
                            column.DataColumn = column.DataObject.Columns.Find(col => col.ID == column.DataObjectColumnID);
                            if (column.DataColumn == null)
                                throw new FasException(DataModelErrorCode.DQ.InitError, $"{unifyCode}获取dataObject:{column.DataObject.Code}的列:{column.DataObjectColumnID}失败，请检查！");
                        }
                    }
                    if (!string.IsNullOrEmpty(column.DataObjectID))
                    {
                        if (column.DataObject == null)
                        {
                            throw new FasException(DataModelErrorCode.DQ.InitError, $"{unifyCode}的列:{column.Name}获取dataObject实体失败，请检查！");
                        }
                    }
                }
                //为datasort填充实例
                if (dataquery.DataQuerySorts != null)
                {
                    foreach (var sort in dataquery.DataQuerySorts.Where(s => s.SortItemType == 0))
                    {
                        //如何判断sort列为自定义列，当SortItemType：0 且 DataObjectID为空时
                        if (!string.IsNullOrEmpty(sort.DataObjectID))
                        {
                            sort.DataObject = dqSources.Find(o => o.ID == sort.DataObjectID);
                            sort.DataColumn = sort.DataObject.Columns.Find(col => col.ID == sort.DataObjectColumnID);
                        }
                        else if (!string.IsNullOrEmpty(sort.DataQueryColumnID))
                        {
                            sort.DataQueryColumn = dataquery.DataQueryColumns.Find(c => c.ID == sort.DataQueryColumnID);
                        }
                    }
                }
                //填充Join实例
                if (dataquery.DataQueryJoins != null)
                {
                    foreach (var join in dataquery.DataQueryJoins)
                    {
                        join.LeftQuerySource = dataquery.DataQuerySources.Find(s => s.ID == join.LeftSourceID);
                        join.RightQuerySource = dataquery.DataQuerySources.Find(s => s.ID == join.RightSourceID);
                    }
                }
                if (dataquery.SubQuerys != null)
                {
                    //填充subquery实例
                    foreach (var subquery in dataquery.SubQuerys)
                    {
                        if (isSubQuery) //子查询DQ不处理SubQuery，否则出现循环引用时会引起堆栈溢出
                            break;
                        //注意不要引起堆栈溢出
                        if (!string.IsNullOrEmpty(subquery.SubQueryID))
                        {
                            if (subquery.SubQuery == null)
                            {
                                subquery.SubQuery = GetMetaDataWithNoDeserialize<DataQuery>(subquery.SubQueryID, false);
                                //填充子查询DO的实体
                                InitializeDQ(subquery.SubQuery, true);
                                if (subquery.SubQuery == null)
                                {
                                    LocalLogService.Write($"{unifyCode}初始化时未找到子查询:{subquery.SubQueryID}的实例，请检查当前是否是导入操作。如果为运行时，此为异常场景 请检查");
                                }
                            }
                        }
                    }
                }
                /* ToDO
                if (dataquery.IsPermissionControl) //兼容json反序列化处理，如果ActionPermission不为空，则为反序列化出来的DQ，直接处理即可
                {
                    //设计时对接权限
                    if (dataquery.ActionPermission == null)
                    {

                        dataquery.ActionPermission = _permissionMgr.GetActionPermissionDefinition(dataquery.ID);
                    }
                }
                else
                {
                    dataquery.ActionPermission = null;
                }
                */
            }
            catch (Exception e)
            {
                throw new FasException(DataModelErrorCode.DQ.InitError, $"{unifyCode}初始化失败，请检查！", e);
            }
        }

        public override IRefSchemaEntity BuildRefSchema(IMetaDataContent metaDataContent)
        {
            if (metaDataContent is DataQuery dq)
            {
                return dq.BuildRefSchema();
            }
            else
            {
                throw new ArgumentNullException(nameof(metaDataContent));
            }
        }

        public override IEnumerable<Metadata.Entity.Term> BuildTerms(IMetaDataContent metaDataContent, IEnumerable<Metadata.Entity.Term> termList = null)
        {
            return default;
        }

        public override IRefSchemaEntity DeserializeRefSchemaEntity(string schemaEntityJson)
        {
            return JsonConvert.DeserializeObject<DataQueryRefSchemaEntity>(schemaEntityJson);
        }

        #region Private Funcs
        private void PublishToBigDataSet(DataQuery dq)
        {
            try
            {
                BigDataDataQueryPublishDTO bigDataDataQueryPublishDTO = MappingToBigDataDataQueryDTO(dq);
                Dictionary<string, string> dic = new Dictionary<string, string>
                {
                    { "objectType", "DataQuery" },
                    { "objectContent", JsonConvert.SerializeObject(bigDataDataQueryPublishDTO) }
                };

                //先调用内部SG发布
                TResponse<string> response = new SGHttpClient(true).PostCurrentIDC<string>(BigDataPublishSG_Inner, dic, 10);
                if (response.state != "1")
                {
                    //再调用外部SG发布
                    SGHttpClient client = new SGHttpClient(false);
                    string domain = CommonUtil.GetCurrentBDPPublishDomain();
                    client.PostRequestDirect<string>(domain, BigDataPublishSG, dic);
                }
            }
            catch (Exception ex)
            {
                throw new Exception($"发布大数据DQ失败:{dq.Code}：\r\n{ex}\r\n ", ex);
            }
        }

        private BigDataDataQueryPublishDTO MappingToBigDataDataQueryDTO(DataQuery dq)
        {
            return new BigDataDataQueryPublishDTO
            {
                ID = dq.ID,
                Code = dq.Code,
                Name = dq.Name,
                Context = SerializeObject(dq, MetadataSerializeType.JSON),
            };
        }
        #endregion

    }

    internal class DataQueryHelper
    {
        private static readonly DatabaseType[] s_bigDataDBType = new DatabaseType[]
        {
            DatabaseType.ES,
            DatabaseType.StarRocks,
            DatabaseType.TiDB,
            DatabaseType.Druid,
            DatabaseType.HBase,
        };

        public static bool IsBigDataQuerySource(DataQuery dq)
        {
            return dq.IsBigDataQuerySource();
            //if (dq == null)
            //{
            //    return false;
            //}
            //else
            //{
            //    var mainSource = dq.DataQuerySources.FirstOrDefault(s => s.IsMainSource);
            //    if (mainSource.DataObject == null)
            //    {
            //        mainSource.DataObject = DataModelRTPUtil.GetRTPMetaData<DataObject>(mainSource.DataObjectID);
            //    }
            //    string dbType = mainSource.DataObject.Database?.DbType;
            //    DatabaseType databaseType = DataTypeUtil.ConvertToDatabaseType(dbType);
            //    if (s_bigDataDBType.Contains(databaseType))
            //    {
            //        return true;
            //    }
            //    else
            //    {
            //        return false;
            //    }
            //}
        }

    }

    internal class BigDataDataQueryPublishDTO
    {
        /// <summary>
        /// ID
        /// </summary>
        public string ID { get; set; }
        /// <summary>
        /// 标识
        /// </summary>
        public string Code { get; set; }
        /// <summary>
        /// 名称
        /// </summary>
        public string Name { get; set; }
        public string Context { get; set; }
    }
}
